﻿using Asp.Versioning;
using FluentValidation;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Quickly.Identity;
using Quickly.Identity.Domain;
using System.Reflection;

namespace Microsoft.Extensions.DependencyInjection;

public static class QuickIdentityBuilderExtensions
{
    public static IdentityBuilder AddQuickIdentity(this IServiceCollection services)
    {
        services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
        services.AddMediatR(cfg =>
        {
            cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
        });

        services.AddScoped<AppUserAccessor>();

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = IdentityConstants.ApplicationScheme;
        });

        services.AddAuthorization();

        services.AddScoped<IQueryableUserStore<AppUser>, UserStore<AppUser>>();
        services.AddScoped<IQueryableRoleStore<AppRole>, RoleStore<AppRole>>();

        return services
            .AddIdentity<AppUser, AppRole>()
            .AddDefaultTokenProviders();
    }

    public static IdentityBuilder AddIdentityQuickly<TDbContext>(this WebApplicationBuilder builder)
        where TDbContext : DbContext
    {
        return builder.AddIdentityQuickly<TDbContext>(options => { });
    }

    public static IServiceCollection AddIdentityEndpointsVersioning(this IServiceCollection services)
    {
        services
            .AddApiVersioning(
                options =>
                {
                    options.ReportApiVersions = true;
                    options.ApiVersionReader = new UrlSegmentApiVersionReader();
                })
            .AddApiExplorer(
                options =>
                {
                    options.GroupNameFormat = "'v'VVV";
                    options.SubstituteApiVersionInUrl = true;
                })
            .EnableApiVersionBinding();

        return services;
    }

    public static IServiceCollection AddIdentityCoreQuickly(this IServiceCollection services)
    {
        services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
        services.AddMediatR(cfg =>
        {
            cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
            //cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>));
            //cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(AuthorizationBehaviour<,>));
            //cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>));
            //cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(PerformanceBehaviour<,>));
        });

        return services;
    }

    public static IdentityBuilder AddIdentityQuickly<TDbContext>(this WebApplicationBuilder builder, Action<IdentityOptions> configure)
        where TDbContext : DbContext
        => AddIdentityQuickly<AppUser, AppRole, TDbContext>(builder, configure);

    public static IdentityBuilder AddIdentityQuickly<TUser, TRole, TDbContext>(this WebApplicationBuilder builder, Action<IdentityOptions> configure)
        where TUser : AppUser
        where TRole : AppRole
        where TDbContext : DbContext
    {
        builder.Services.AddIdentityEndpointsVersioning();

        builder.Services.AddScoped<AppUserAccessor>();
        builder.Services.AddTransient<IEmailSender<TUser>, EmptyEmailSender<TUser>>();

        var identityBuilder = builder.Services
            .AddIdentity<TUser, TRole>(
                options =>
                {
                    configure?.Invoke(options);
                })
            .AddApiEndpoints()
            .AddEntityFrameworkStores<TDbContext>();


        builder.Services.AddTransient<IQueryableUserStore<TUser>, UserStore<TUser, TRole, TDbContext, string>>();
        builder.Services.AddTransient<IQueryableRoleStore<TRole>, RoleStore<TRole, TDbContext, string>>();

        builder.Services.AddIdentityCoreQuickly();

        builder.Services.ConfigureApplicationCookie(options =>
        {
            ConfigureLoginPathifConfigured(options, builder.Configuration);
        });

        builder.Services
            .AddAuthentication();

        builder.Services.AddAuthorization();

        return identityBuilder;
    }

    public static void ConfigureLoginPathifConfigured(CookieAuthenticationOptions options, IConfiguration configuration)
    {
        var section = configuration.GetSection("LoginPath");

        if (section is not null && string.IsNullOrWhiteSpace(section.Value) == false)
        {
            options.LoginPath = section.Value;
        }
    }

    public static WebApplication MapIdentityApiQuickly(this WebApplication app, string? prefixGroup = null) => MapIdentityApiQuickly<AppUser>(app, prefixGroup);

    public static WebApplication MapIdentityApiQuickly<TUser>(this WebApplication app, string? preifxGroup = null)
        where TUser : class, new()
    {
        var identity = app.NewVersionedApi();

        var v1 = identity.MapGroup(preifxGroup ?? "/api/identity/v{version:apiVersion}").HasApiVersion(SupportApiVersions.V1);
        v1.MapIdentityApi<TUser>();

        var v2 = identity.MapGroup(preifxGroup ?? "/api/identity/v{version:apiVersion}").HasApiVersion(SupportApiVersions.V2);

        return app;
    }
}
